#include "clientHandler.h"
#include <QString>

using namespace std;

const QString authorization = "Auth";
const QString registration = "Regs";
const QString message = "Msg";
const QString groupMessage = "Gmsg";
const QString roster = "Rstr";
const QString budPresence = "BudPr";
const QString presence = "Prsnc";
const QString addContact = "AdC";
const QString removeContact = "RmC";
const QString noContact = "does not exist";

ClientHandler :: ClientHandler(QVector<QTcpSocket *> *theClientsConnected, QTcpSocket *clientSocket)
{

    mySocket = clientSocket;
    allSockets = theClientsConnected;
    connect(mySocket, SIGNAL(readyRead()), this, SLOT(handleIncomingData()));
    connect(mySocket, SIGNAL(disconnected()), this, SLOT(handleDisconnection()));

}


void ClientHandler::handleDisconnection()
{
    qDebug() << "Disconnected: " << mySocket->peerAddress() << ":" << mySocket->peerPort() << endl << flush;
    for(int i=0; i<allSockets->size(); i++){
        if(mySocket == allSockets->at(i))
            allSockets->remove(i);
    }
    User::userInstance()->removeSocketId(username);
    mySocket->close();
    this->deleteLater();
}


void ClientHandler::handleIncomingData()
{
    QDataStream incomingData(mySocket);
    incomingData.setVersion(QDataStream::Qt_4_5);

    nextBlockSize =0;
    // delete the next 3 debugs
    qDebug() << "data stream status" << incomingData.status() << endl << flush;
    qDebug() << "nextBlockSize: " << nextBlockSize;
    qDebug() << " btes: " << mySocket->bytesAvailable();


    if(nextBlockSize == 0) {
        if(mySocket->bytesAvailable() < sizeof(quint16))
        {
            qDebug() << "Bytes avail triggered quit of function, avail: " << mySocket->bytesAvailable();
            return;
        }

        incomingData >> nextBlockSize;
        if(nextBlockSize == 0) return;
        qDebug() << "this is the new block size " << nextBlockSize;
    }

    if(mySocket->bytesAvailable()<nextBlockSize) {
        qDebug() << "Exiting";
        return;
    }


    qDebug() << "DEBUG data stream "<< incomingData.status();
    // call tag reader to see what kind of stream was received
    tagReader(incomingData,(int) mySocket->peerPort());
    qDebug() << "this is after tagreader";
    // reset the data stream to get new streams

    incomingData.resetStatus();


}



void ClientHandler::tagReader(QDataStream &theData, int socketId){

    theData >> tag;

    qDebug() << "the tag: " << tag << endl;

    if(QString::compare(tag, authorization, Qt::CaseSensitive)==0)
        getAuthorization(theData, socketId);

    else if(QString::compare(tag, registration, Qt::CaseSensitive)==0)
        registerUser(theData, socketId);

    else if(QString::compare(tag, message, Qt::CaseSensitive)==0)
        getMessage(theData, socketId);

    else if(QString::compare(tag, groupMessage, Qt::CaseSensitive)==0)
        cout << "groupMessage" << endl;

    else if(QString::compare(tag, roster, Qt::CaseSensitive)==0)
        sendBuddies();

    else if(QString::compare(tag, presence, Qt::CaseSensitive)==0)
        getPresence(theData);

    else if(QString::compare(tag, addContact, Qt::CaseSensitive)==0)
        getAddContact(theData);

    else if(QString::compare(tag, removeContact, Qt::CaseSensitive)==0)
        getRemoveContact(theData);

    else if(QString::compare(tag, "") == 0) {
        QString debugMsg;
        theData >> debugMsg;
        theData.resetStatus();
        qDebug() << "this is the all stream -> " << tag << debugMsg;
    }


    return;
}


void ClientHandler::getAuthorization(QDataStream &theData, int socketId){

    int id;

    theData >> username >> password;


    qDebug() << username << endl << flush;
    qDebug() << password << endl << flush;



    //check database with this info
    if(User::userInstance()->verifyLoginInfo(username, password, socketId) == true)
        id = User::userInstance()->getIdFromName(username);
    else
        id = -1;

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << quint16(0) << authorization << id; // tag to const
    out.device()->seek(0);
    out << quint16(block.size() - sizeof(quint16));
    mySocket->write(block);
    qDebug() << "Authorization stage completed" << endl << flush;


    return;
}

void ClientHandler::registerUser(QDataStream &theData, int socketId){

    theData >> username >> password >> email;
    qDebug() << username << endl << flush;
    qDebug() << password << endl << flush;
    qDebug() << email << endl << flush;
    //add a user to the database with this info
    int result = User::userInstance()->registerNewUser(username, password, email, socketId);
    qDebug() << result << endl << flush;
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << quint16(0) << registration << result;
    out.device()->seek(0);
    out << quint16(block.size() - sizeof(quint16));
    mySocket->write(block);
    return;

    //add a user to the database with this info


}

void ClientHandler::getMessage(QDataStream &theData, int socketId){


    theData >> from >> to >>theMessage;

    qDebug() << theMessage << endl << flush;

    writeToClients();

    return;
}

void ClientHandler::getPresence(QDataStream &theData)
{
    theData >> username >> status;
    qDebug() << username << endl << flush;
    qDebug() << status << endl << flush;
    //    writeToBuddylists(status);

    return;
}

void ClientHandler::getAddContact(QDataStream &theData)
{
    theData >> username >> screenName >> email;

    if(QString::compare(screenName, "") != 0) {

        qDebug() << screenName << endl << flush;
        if( User::userInstance()->addBuddy(username, screenName))
            sendBuddies();

    }
    else if(QString::compare(email, "") != 0) {

        qDebug() << screenName << email << flush;
        if(  User::userInstance()->addBuddy(username, email) )
            sendBuddies();

    }

    else
        sendNoContacts();

    return;
}

void ClientHandler::sendNoContacts()
{
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << quint16(0) << addContact << noContact;
    out.device()->seek(0);
    out << quint16(block.size() - sizeof(quint16));
    mySocket->write(block);
    return;
}

void ClientHandler::getRemoveContact(QDataStream &theData)
{
    theData >> username >> screenName;
    qDebug() << username << endl << flush;
    qDebug() << screenName << endl << flush;
    if(User::userInstance()->removeBuddy(username, screenName))
        sendBuddies();
    return;
}

void ClientHandler::writeToClients()
{
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << quint16(0) << message << from << to << theMessage;
    out.device()->seek(0);
    out << quint16(block.size() - sizeof(quint16));
    qDebug() <<"This is what the serves is sending back: " << message << " "
            << from << " " << to << " " << theMessage << endl << flush;
    int peerSocket;
    peerSocket =  User::userInstance()->getSocketId(to);


    for (int i=0; i<allSockets->size(); i++) {        
        qDebug() << to;
        if (peerSocket == (int) allSockets->at(i)->peerPort()) {
            allSockets->at(i)->write(block);
            break;
        }

    }
    mySocket->write(block);
}

void ClientHandler::sendBuddies()
{
    QVector<QString> allMyBuddies = User::userInstance()->retrieveBuddies(this->username);
    qDebug() << "buddies of: " << this->username;
    qDebug() << "this is the debug for my buddies" << endl << flush;

    QString theBuds = "";
    qDebug() << "this is the end of debug for my buddies" << endl << flush;

    for(int i=0; i<allMyBuddies.size(); i++) {
        if(QString::compare(allMyBuddies.at(i), "") == 0 ){
            allMyBuddies.remove(i);

        } theBuds.append(allMyBuddies.at(i) + ";");
    }
    qDebug() << theBuds;
    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << quint16(0) << roster << theBuds;
    out.device()->seek(0);
    out << quint16(block.size() - sizeof(quint16));
    mySocket->write(block);

    for(int i=0; i<allMyBuddies.size(); i++) {
        qDebug() << allMyBuddies.at(i);
    }

    qDebug() << "roster and buddies status have been sent";

    return;
}
